package com.bobi.ecofavor.service.impl;

import cn.hutool.core.bean.BeanUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.bobi.ecofavor.common.enums.*;
import com.bobi.ecofavor.common.utils.JsonResponse;
import com.bobi.ecofavor.common.utils.RedisUtil;
import com.bobi.ecofavor.common.utils.UUIDUtil;
import com.bobi.ecofavor.model.domain.*;
import com.bobi.ecofavor.mapper.OrderMapper;
import com.bobi.ecofavor.model.dto.CouponDto;
import com.bobi.ecofavor.model.dto.OrderDto;
import com.bobi.ecofavor.model.vo.OrderVO;
import com.bobi.ecofavor.service.*;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;

/**
 * <p>
 * 订单 服务实现类
 * </p>
 *
 * @author wenyuan
 * @since 2023-10-21
 */
@Service
public class OrderServiceImpl extends ServiceImpl<OrderMapper, Order> implements OrderService {

    @Autowired
    private OrderMapper orderMapper;

    @Autowired
    private GoodsService goodsService;

    @Autowired
    private TradeService tradeService;

    @Autowired
    private CouponService couponService;

    @Autowired
    private CouponCategoryService couponCategoryService;

    @Autowired
    private StoreService storeService;

    @Autowired
    private RedisUtil redisUtil;

    @Transactional
    @Override
    public JsonResponse getUserOrderListPage(Integer userId, Integer pageSize, Integer page, Integer status) {
        // 1. 创建分页对象
        // Integer currentPage = (page - 1) * pageSize;
        Integer currentPage = page;
        Page<Order> orderPage = new Page<>(currentPage, pageSize);

        // 2. 根据订单状态分页，查询所有父订单，父订单的 parentOrder为空
        LambdaQueryWrapper<Order> orderQueryWrapper = new LambdaQueryWrapper<>();
        orderQueryWrapper.eq(Order::getUserId, userId)
                .isNull(Order::getParentOrder)
                .orderByDesc(Order::getOrderTime);
        if(!OrderStatusEnum.isALL(status))
            orderQueryWrapper.eq(Order::getOrderStatus, status);
        page(orderPage, orderQueryWrapper);

        // 3. 转换为Dto
        Page<OrderVO> orderListPage = getOrderListPage(pageSize, currentPage, orderPage);

        return JsonResponse.success(orderListPage);
    }

    /**
     *  转换为Dto
     */
    private Page<OrderVO> getOrderListPage(Integer pageSize, Integer currentPage, Page<Order> orderPage) {
        Page<OrderVO> orderListVOPage = new Page<>(currentPage, pageSize);

        List<OrderVO> orderVOs = orderPage.getRecords().stream()
            .map(item -> getOrderVO(item))
            .collect(Collectors.toList());
        orderListVOPage.setRecords(orderVOs);
        orderListVOPage.setTotal(orderPage.getTotal());
        return orderListVOPage;
    }

    private OrderVO getOrderVO(Order item) {
        OrderVO orderVO = new OrderVO();
        // 1. 赋值订单和子订单总计价格，以及优惠价总计等，这些都是在创建订单（即是将商品添加进入订单时）和选择优惠券时计算好存进了父订单里
        OrderDto orderDto = BeanUtil.copyProperties(item, OrderDto.class);

        // 2. 查询所有子订单
        List<Order> orderList = getOrderChildren(item.getId());
        List<OrderDto> orderDtoList = orderList.stream().map(element -> BeanUtil.copyProperties(element, OrderDto.class)).collect(Collectors.toList());

        // 3. 设置订单的状态代号代表的意思
        String desc = OrderStatusEnum.getDescByCode(item.getOrderStatus());
        if (desc == null)
            desc = "未知";
        orderDto.setStatusMean(desc);
        for (OrderDto dto : orderDtoList)
            dto.setStatusMean(desc);

        orderVO.setOrder(orderDto);
        orderVO.setOrderItemList(orderDtoList);
        return orderVO;
    }

    /**
     * 修改订单状态
     */
    @Transactional
    @Override
    public JsonResponse updateOrderStatus(Integer id, Integer status) {
        // 1. 查询所有子订单
        List<Order> orderItemList = getOrderChildren(id);
        // 2. 如果有子订单说明它是一个父订单
        if (orderItemList != null && orderItemList.size() > 0) {
            for (Order orderItem : orderItemList) {
                // 2.1. 更新子订单状态
                orderItem.setOrderStatus(status);
                this.updateById(orderItem);

                // 2.2. 如果是取消订单，则应该将库存还回去
                if (status == OrderStatusEnum.CANCELLED.getCode()) {
                    goodsService.recoverStock(orderItem.getId(), orderItem.getGoodsNum().intValue());
                }
            }

            // 2.3. 更新订单的状态，并清空选择使用的优惠券
            LambdaUpdateWrapper<Order> updateWrapper = new LambdaUpdateWrapper<>();
            updateWrapper.eq(Order::getId, id)
                    .set(Order::getOrderStatus, status)
                    .set(Order::getSelectedCoupons, "");
            this.update(null, updateWrapper);
        } else {        // 3. 没有子订单，则它是一个子订单，取消子订单应该判断是否是取消最后一个子订单
            // 更改子订单的状态只有取消也就是删除，新增子订单 在 创建订单里
            // 归还库存
            // 其实没有必要进行判断是否是取消，因为子订单在这里只会是取消状态
            Order order = this.getById(id);
            Order parentOrder = orderMapper.getParentOrder(id);
            // 更新子订单状态
            order.setOrderStatus(status);
            this.updateById(order);

            if (status == OrderStatusEnum.CANCELLED.getCode()) {
                goodsService.recoverStock(id, order.getGoodsNum().intValue());
                // 4. important 重新计算金额，避免新增子订单或者删除子订单，让之前使用的优惠券打折券，不适应
                BigDecimal totalMoney = parentOrder.getTotalMoney();
                // 总金额等于 父订单先前的金额减去 订单的原价乘以商品的数量
                totalMoney = totalMoney.subtract(order.getTotalMoney().multiply(order.getGoodsNum()));

                // 不满足条件的优惠券应该取消选取，重新设置父订单选择使用的优惠券
                User user = (User) redisUtil.get(CacheKeyEnum.USER.getKey());
                Integer userId = user.getId();
                List<CouponDto> unusedCouponDtos = getUnusedCouponDtos(userId, parentOrder);
                checkAndComputeOrder(parentOrder, totalMoney, unusedCouponDtos);
                this.updateById(parentOrder);
            }

            // 5. 查询跟的兄弟订单，是否还有状态为待支付的
            List<Order> brotherOrders = orderMapper.selectBrotherOrders(id);
            // 如果没有了，就取消父订单
            if (brotherOrders == null || brotherOrders.size() == 0) {
                orderMapper.cancleParentOrder(id);
                JsonResponse<Object> success = JsonResponse.success(null, ResponseCodeEnum.NONE.getMsg());
                success.setCode(ResponseCodeEnum.NONE.getCode());
                return success;
            }
            // 如果还有就没关系
        }

        return JsonResponse.success(null);
    }

    /**
     * 根据现金额计算是否满足选取的优惠券，不满足的取消选取，然后根据选取的优惠券计算订单金额
     * @param parentOrder
     * @param totalMoney
     * @param unusedCouponDtos
     */
    private void checkAndComputeOrder(Order parentOrder, BigDecimal totalMoney, List<CouponDto> unusedCouponDtos) {
        String selectedCoupons = parentOrder.getSelectedCoupons();
        // 有选择使用的优惠券才重新计算
        if (selectedCoupons != null && !selectedCoupons.isEmpty()) {
            String[] selectedCouponArr = selectedCoupons.split(",");
            ArrayList<String> selectedCouponList = new ArrayList<>(Arrays.asList(selectedCoupons));
            for (String selectedCouponId : selectedCouponList) {
                // 如果选择使用的优惠券不在可使用的优惠券里，就代表不满足条件了
                Boolean isExisted = false;      // 存在设为true
                for (CouponDto unusedCouponDto : unusedCouponDtos) {
                    if (unusedCouponDto.getId() == Integer.valueOf(selectedCouponId)) {
                        isExisted = true;
                        break;
                    }
                }
                if (!isExisted)
                    selectedCouponList.remove(selectedCouponId);
            }

            // 将选择使用的优惠券列表转为字符串
            String couponStr = String.join(",", selectedCouponList);
            parentOrder.setSelectedCoupons(couponStr);
            recomputeBill(parentOrder, totalMoney);
        }
    }

    private List<Order> getOrderChildren(Integer id) {
        LambdaQueryWrapper<Order> orderQueryWrapper = new LambdaQueryWrapper<>();
        // 查询状态不为取消的子订单，包括待支付、支付、完成
        // 这表明了取消的订单不会出现在父订单里
        orderQueryWrapper.eq(Order::getParentOrder, id)
                .ne(Order::getOrderStatus, OrderStatusEnum.CANCELLED.getCode());
        List<Order> orderItemList = this.list(orderQueryWrapper);
        return orderItemList;
    }

    /**
     * 根据订单id 查询订单
     * @param orderId 订单id
     * @return JsonResponse
     */
    @Transactional
    @Override
    public JsonResponse getUserOrderById(Integer uid, Integer orderId) {
        // 1. 查询订单表，并将交易的总计金额、优惠金额等信息转为OrderVO
        Order order = getById(orderId);
        OrderVO orderVO = getOrderVO(order);

        // 2. 查询可使用的优惠券
        List<CouponDto> unsedCoupons = getUnusedCouponDtos(uid, order);

        // 3. 将可用优惠券列表存入订单里，
        OrderDto parentOrder = orderVO.getOrder();
        parentOrder.setUseableCoupons(unsedCoupons);

        // 4. 将选择使用的优惠券的id拼接字符串转为数组
        String selectedCoupons = order.getSelectedCoupons();
        if (selectedCoupons == null || selectedCoupons.isEmpty()) {
            parentOrder.setSelectedCoupons(new ArrayList<>());
        } else {
            String[] ids = selectedCoupons.split(",");
            List<String> idList = Arrays.asList(ids);
            parentOrder.setSelectedCoupons(idList);
        }

        return JsonResponse.success(orderVO);
    }

    private List<CouponDto> getUnusedCouponDtos(Integer uid, Order order) {
        List<CouponDto> unsedCoupons = couponService.getUnsedCoupons(uid);

        // 得到订单的种类和店铺id
        Integer storeCategory = order.getStoreCategory();
        Integer storeId = order.getStoreId();

        // 3. 得到在当前订单下可使用的优惠券列表
        if (unsedCoupons != null) {
            unsedCoupons.stream().filter(item -> {
                // 全平台通用
                if (item.getUseRange() == CouponRangeEnum.COMMON.getCode()) {
                    return isCoverCondition(order, item);
                }
                // 指定分类
                else if (item.getUseRange() == CouponRangeEnum.CLASS.getCode()) {
                    // 如果符合分类
                    if (item.getStoreCategory() == storeCategory)
                        return isCoverCondition(order, item);
                    // 不符合分类
                    return false;
                }
                else {
                    // 如果符合指定店铺
                    if (item.getStoreId() == storeId)
                        return isCoverCondition(order, item);
                    return false;
                }
            });
            return unsedCoupons;
        }
        return new ArrayList<>();
    }

    /**
     * 赋值订单和子订单总计价格，以及优惠价总计等，这些都是在创建订单（即是将商品添加进入订单时）计算好存进了父订单里
     * @return JsonResponse
     */
    @Override
    public JsonResponse insertOrder(User user, Integer goodsId, Integer goodsNum) {
        Order order = new Order();
        Goods goods = goodsService.getById(goodsId);
        Store store = storeService.getById(goods.getStoreId());

        // 1. 是否已经有了一个未支付的父订单
        LambdaQueryWrapper<Order> orderQueryWrapper = new LambdaQueryWrapper<>();
        orderQueryWrapper.isNull(Order::getParentOrder)
                .eq(Order::getOrderStatus, OrderStatusEnum.UNPAID.getCode())
                .eq(Order::getStoreId, store.getId());
        Order parentOrder = this.getOne(orderQueryWrapper);

        // 2. 如果父订单为空，则创建一个父订单，不为空就计算金额
        if (parentOrder == null) {
            parentOrder = new Order();
            setOrderAttr(user, goods, store, parentOrder);
            this.save(parentOrder); // 保存到数据库，以便子订单填写父订单id
        } else {
            BigDecimal totalMoney = parentOrder.getTotalMoney();
            totalMoney = totalMoney.add(goods.getPrice().multiply(BigDecimal.valueOf(goodsNum)));
            BigDecimal settlementMoney = parentOrder.getSettlementMoney();
            settlementMoney = settlementMoney.add(goods.getPrice());
            parentOrder.setTotalMoney(totalMoney)
                            .setSettlementMoney(settlementMoney);

            // 3. important 重新计算金额，避免新增子订单或者删除子订单，让之前使用的优惠券打折券，不适应
            recomputeBill(parentOrder, totalMoney);
            this.updateById(parentOrder);
        }

        // 4. 设置子订单内容，
        order.setParentOrder(parentOrder.getId());
        setOrderAttr(user, goods, store, order);
        // 子订单还需设置商品信息
        order.setGoodsId(goods.getId())
                .setGoodsImage(goods.getGoodsPhoto())
                .setGoodsName(goods.getGoodsName())
                .setGoodsParam(goods.getParameter())
                .setGoodsNum(BigDecimal.valueOf(goodsNum))
                .setGoodsCategory(goods.getGoodsCategory());  // 条件限制，商品分类跟店铺分类一样

        this.save(order);

        // 5. 扣减商品库存
        goods.setGoodsQuantity(goods.getGoodsQuantity() - goodsNum);
        goodsService.updateById(goods);

        return JsonResponse.success(order.getId());
    }

    /**
     * 根据父订单和新的总金额，基于选择使用的优惠券进行重新计算
     * @param parentOrder
     * @param totalMoney
     */
    private void recomputeBill(Order parentOrder, BigDecimal totalMoney) {
        parentOrder.setTotalMoney(totalMoney);
        // 1. 先获取选择使用的优惠券
        String selectedCoupons = parentOrder.getSelectedCoupons();
        // 如果没有使用优惠券就不需要计算
        if (selectedCoupons != null && selectedCoupons.length() > 0) {
            String[] couponIds = selectedCoupons.split(",");
            for (String couponId : couponIds) {
                computeBill(parentOrder, couponId);
            }
        }

    }

    private static void setOrderAttr(User user, Goods goods, Store store, Order order) {
        order.setOrderSn(UUIDUtil.provideUUID())
                .setStoreId(store.getId())
                .setStoreName(store.getStoreName())
                .setStorePhone(store.getPhoneNumber())
                .setStoreCategory(store.getStoreCategory())
                .setOrderTime(LocalDateTime.now())
                .setOrderStatus(OrderStatusEnum.UNPAID.getCode())
                .setPaymentMod(PaymentModEnum.WX.getCode())
                .setUserId(user.getId())
                .setUserPhone(user.getPhoneNumber())
                .setUserName(user.getNickname())
                .setTotalMoney(goods.getPrice())
                .setSettlementMoney(goods.getPrice());
    }

    /**
     * 优惠券扣减金额 且改变总计金额 以及订单使用了这些优惠券，只是存入订单里，不改变优惠券状态，订单结账时才改变
     * note: 1. 打折跟满减不能同时使用
     * 2. 打折是累成，满减也是累减，计算基础都是基于原始价格
     * @param couponId 优惠券id
     * @param orderId 订单id
     * @return JsonResponse
     */
    @Transactional
    @Override
    public JsonResponse useCouponInOrder(Integer couponId, Integer orderId) {
        // 1. 查询订单
        Order order = getById(orderId);

        // 2. 判断优惠券是否已经有了，如果已经有了则是取消使用优惠券
        String couponStr = order.getSelectedCoupons();
        List<String> newCouponIdList = new ArrayList<>();

        if (couponStr == null || couponStr.isEmpty()) {
            couponStr = couponId.toString();
            newCouponIdList.add(couponId.toString());
        } else {
            String[] selectedCoupons = couponStr.split(",");
            ArrayList<String> couponIds = new ArrayList<>(Arrays.asList(selectedCoupons));
            Boolean isExisted = false;
            // 循环移除元素会导致空指针移除，好在这里，有且仅有一个相同id
            for (String id : couponIds) {
                if (couponId == Integer.valueOf(id)) {
                    couponIds.remove(id);
                    isExisted = true;
                    break;
                }
            }
            if (!isExisted)
                couponIds.add(couponId.toString());
            newCouponIdList = couponIds;
            couponStr = String.join(",", newCouponIdList);
        }
        order.setSelectedCoupons(couponStr);


        // 3. 计算金额
        List<Order> orderList = getOrderChildren(orderId);
        // 3.1. 如果没有子订单，直接返回，没有子订单时没有商品的，且父订单为取消状态，一般来说不会有详情页面
        if (orderList == null || orderList.size() == 0) {

        } else {
            // 3.2. 如果有子订单
            // 3.2.1 先获取子订单
            BigDecimal totalMoney = BigDecimal.ZERO;
            // 3.2.2 计算子订单和该订单总计金额
            for (Order item : orderList) {
                totalMoney = totalMoney.add(item.getTotalMoney());
            }

            //  计算优惠券扣减金额
            // 如果是取消使用最后一张优惠券
            if (newCouponIdList.size() == 0) {
                order.setTotalMoney(totalMoney);
                order.setDiscountMoney(BigDecimal.ZERO);
                order.setSettlementMoney(totalMoney);
            } else {
                for (String item : newCouponIdList)
                    computeBill(order, item);
            }
        }

        // 4. 更新订单
        this.updateById(order);

        return JsonResponse.success(null);
    }

    /**
     * 付款，1. 微信支付（条件限制，不做）  2. 更改交易信息 3. 更改订单状态 4. 更改优惠券状态 5. 更新店铺的销售数量
     */
    @Transactional
    @Override
    public JsonResponse payBill(Integer orderId) {
        // 1. 微信支付（条件限制，不做）

        // 2. 设置交易信息
        Order order = getById(orderId);

        Trade trade = new Trade();
        BeanUtil.copyProperties(order, trade, "id", "createTime", "updateTime", "isDeleted");
        trade.setTradeSn(UUIDUtil.provideUUID());
        trade.setOrderId(order.getId());
        trade.setTotalAmount(order.getTotalMoney());
        trade.setDiscountAmount(order.getDiscountMoney());
        trade.setTradeAmount(order.getSettlementMoney());
        trade.setPayTime(LocalDateTime.now());
        tradeService.save(trade);

        // 3. 更改订单状态
        updateOrderStatus(orderId, OrderStatusEnum.PAID.getCode());

        // 4. 更改优惠券状态 和 使用时间
        // 4.1. 查询订使用的优惠券
        String selectedCoupons = order.getSelectedCoupons();
        if (selectedCoupons != null && !selectedCoupons.isEmpty()) {
            String[] couponIds = selectedCoupons.split(",");
            // 4.2. 更新，没有值的属性不会覆盖数据库有值的，除非加个注解 @TableField(updateStrategy = FieldStrategy.IGNORED)
            for (String couponId : couponIds) {
                Coupon coupon = new Coupon();
                coupon.setId(Integer.valueOf(couponId));
                coupon.setCouponStatus(CouponStatusEnum.USED.getCode());
                coupon.setUseTime(LocalDateTime.now());
                couponService.updateById(coupon);
            }
        }

        // 5. 更新店铺的销售数量
        Integer storeId = order.getStoreId();
        LambdaUpdateWrapper<Store> storeUpdateWrapper = new LambdaUpdateWrapper<>();
        storeUpdateWrapper.eq(Store::getId, storeId)
                        .setSql("order_num = order_num + 1");
        storeService.update(storeUpdateWrapper);

        return JsonResponse.success(null);
    }

    /**
     * 再来一单
     * @param id 父订单id
     * @return JsonResponse
     */
    @Override
    public JsonResponse oneMore(Integer id) {
        // 1. 查询父订单
        Order parentOrder = this.getById(id);
        Order newParentOrder = BeanUtil.copyProperties(parentOrder, Order.class
                , "id", "orderSn", "updateTime", "orderTime", "finishTime", "orderStatus"
                , "createTime", "isDeleted", "scheduledTime", "isReserved", "selectedCoupons", "discountMoney", "settlementMoney");

        newParentOrder.setOrderTime(LocalDateTime.now());
        newParentOrder.setOrderSn(UUIDUtil.provideUUID());
        // 2. 获取所有子订单
        List<Order> orderChildren = getOrderChildren(id);
        List<Order> newOrderChildren = orderChildren.stream().map(item -> BeanUtil.copyProperties(item, Order.class
                , "id", "orderSn", "updateTime", "orderTime", "finishTime", "orderStatus"
                , "createTime", "isDeleted", "scheduledTime", "isReserved", "selectedCoupons"
                , "parentOrder")).collect(Collectors.toList());
        newOrderChildren.forEach(item -> {
            item.setOrderTime(LocalDateTime.now());
            item.setOrderSn(UUIDUtil.provideUUID());
        });

        // 3. 保存数据
        this.save(newParentOrder);
        this.saveBatch(newOrderChildren);

        // 4. 更新父订单结算金额
        newParentOrder.setSettlementMoney(newParentOrder.getTotalMoney());
        this.updateById(newParentOrder);

        // 5. 更新子订单的父订单id
        for (Order newOrderChild : newOrderChildren) {
            newOrderChild.setParentOrder(newParentOrder.getId());
            this.updateById(newOrderChild);
        }

        Integer orderId = newParentOrder.getId();
        return JsonResponse.success(orderId);
    }

    /**
     * 计算订单的扣减金额和订单的借宿那金额
     * @param order 订单
     * @param item 优惠券id
     */
    private void computeBill(Order order, String item) {
        BigDecimal totalMoney = order.getTotalMoney();
        BigDecimal result = BigDecimal.ZERO;
        BigDecimal discount = BigDecimal.ZERO;
        System.out.println("totalMoney------" + totalMoney);

        // 1. 查询优惠券
        Coupon coupon = couponService.getById(Integer.valueOf(item));
        // 2. 查询优惠券种类得到扣减金额
        CouponCategory couponCategory = couponCategoryService.getById(coupon.getCouponCategoryId());
        // 3. 根据优惠券优惠类型来计算
        // 3.1. 如果是打折
        if (couponCategory.getCouponCondition() == CouponConditionEnum.DISCOUNT.getCode()) {
            //  原价100 九折 -> 100 * 90 / 100
            result = totalMoney.multiply(couponCategory.getDiscount()).divide(BigDecimal.valueOf(100));
            discount = totalMoney.subtract(result);
        } else {
            result = totalMoney.subtract(couponCategory.getCutAmount());
            discount = totalMoney.subtract(result);
        }
        // 4. 设置订单金额
        order.setDiscountMoney(discount);
        order.setSettlementMoney(result);

        System.out.println("totalMoney------" + totalMoney);
    }


    private boolean isCoverCondition (Order order, CouponDto item) {
        // 如果满减，必须满足金额
        if (item.getCouponCondition() == CouponConditionEnum.FULLCUT.getCode()) {
            if (item.getRequiredAmount().compareTo(order.getTotalMoney()) <= 0)
                return true;
            else return false;
        }
        // 打折的不需要
        return true;
    }
}
